/*
 * @(#)ByteToCharISO2022.java	1.11 06/10/10
 *
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 *
 */

package sun.io;

/**
 * An algorithmic conversion from ISO 2022 to Unicode
 *
 * @author Tom Zhou
 */
public abstract class ByteToCharISO2022 extends ByteToCharConverter {
    // Value to be filled by subclass
    protected String SODesignator[];
    protected String SS2Designator[] = null;
    protected String SS3Designator[] = null;
    protected ByteToCharConverter SOConverter[];
    protected ByteToCharConverter SS2Converter[] = null;
    protected ByteToCharConverter SS3Converter[] = null;
    private static final byte ISO_ESC = 0x1b;
    private static final byte ISO_SI = 0x0f;
    private static final byte ISO_SO = 0x0e;
    private static final byte ISO_SS2_7 = 0x4e;
    private static final byte ISO_SS3_7 = 0x4f;
    private static final byte MSB = (byte) 0x80;
    private static final char REPLACE_CHAR = '\uFFFD';
    private static final byte maximumDesignatorLength = 3; 
    private static final byte SOFlag = 0;
    private static final byte SS2Flag = 1;
    private static final byte SS3Flag = 2;
    private static final byte G0 = 0;
    private static final byte G1 = 1;
    private ByteToCharConverter tmpConverter[];
    private int curSODes, curSS2Des, curSS3Des;
    private boolean shiftout;
    private byte remainByte[] = new byte[10];
    private int remainIndex = -1;
    private byte state, firstByte;
    public void reset() {
        int i = 0;
        shiftout = false;
        state = G0;
        firstByte = 0;
        curSODes = 0;
        curSS2Des = 0;
        curSS3Des = 0;
        charOff = byteOff = 0;
        remainIndex = -1;
        for (i = 0; i < remainByte.length; i++)
            remainByte[i] = 0;
    }

    public int flush(char[] output, int outStart, int outEnd)
        throws MalformedInputException {
        int i;
        if (state != G0) {
            badInputLength = 0;
            throw new MalformedInputException();
        }
        reset();
        return 0;
    }

    private byte[] savetyGetSrc(byte[] input, int inOff, int inEnd, int nbytes) {
        int i;
        byte tmp[];
        if (inOff <= (inEnd - nbytes + 1))
            tmp = new byte[nbytes];
        else
            tmp = new byte[inEnd - inOff];
        for (i = 0; i < tmp.length; i++)
            tmp[i] = input[inOff + i];
        return tmp; 	
    }

    private char getUnicode(byte byte1, byte byte2, byte shiftFlag) {
        byte1 |= MSB;
        byte2 |= MSB;
        byte[] tmpByte = {byte1, byte2};
        char[] tmpChar = new char[1];
        int     i = 0,
            tmpIndex = 0;
        switch (shiftFlag) {
        case SOFlag:
            tmpIndex = curSODes;
            tmpConverter = (ByteToCharConverter[]) SOConverter;
            break;

        case SS2Flag:	
            tmpIndex = curSS2Des;
            tmpConverter = (ByteToCharConverter[]) SS2Converter;
            break;

        case SS3Flag:
            tmpIndex = curSS3Des;
            tmpConverter = (ByteToCharConverter[]) SS3Converter;
            break;
        }
        for (i = 0; i < tmpConverter.length; i++) {
            if (tmpIndex == i) {
                try {
                    tmpConverter[i].convert(tmpByte, 0, 2, tmpChar, 0, 1);
                } catch (Exception e) {}
                return tmpChar[0];
            }
        }
        return REPLACE_CHAR;
    }

    public final int convert(byte[] input, int inOff, int inEnd,
        char[] output, int outOff, int outEnd)
        throws ConversionBufferFullException {
        int i;
        int DesignatorLength = 0;
        charOff = outOff;
        byteOff = inOff;
        // Loop until we hit the end of the input
        while (byteOff < inEnd) {
            // If we don't have room for the output, throw an exception
            if (charOff >= outEnd)
                throw new ConversionBufferFullException();
            if (remainIndex < 0) {
                remainByte[0] = input[byteOff];
                remainIndex = 0;
                byteOff++;
            }
            switch (remainByte[0]) {
            case ISO_SO:
                shiftout = true;
                if (remainIndex > 0)
                    System.arraycopy(remainByte, 1, remainByte, 0, remainIndex);
                remainIndex--;
                break;

            case ISO_SI:
                shiftout = false;
                if (remainIndex > 0)
                    System.arraycopy(remainByte, 1, remainByte, 0, remainIndex);
                remainIndex--;
                break;

            case ISO_ESC:
                byte tmp[] = savetyGetSrc(input, byteOff, inEnd,
                        (maximumDesignatorLength - remainIndex));
                System.arraycopy(tmp, 0, remainByte, remainIndex + 1, tmp.length);
                remainIndex += tmp.length;
                byteOff += tmp.length;
                if (tmp.length < (maximumDesignatorLength - remainIndex))
                    break;
                String tmpString = new String(remainByte, 1, remainIndex);
                for (i = 0; i < SODesignator.length; i++) {
                    if (tmpString.indexOf(SODesignator[i]) == 0) {
                        curSODes = i;
                        DesignatorLength = SODesignator[i].length();
                        break;
                    }
                }
                if (i == SODesignator.length) {
                    for (i = 0; i < SS2Designator.length; i++) {
                        if (tmpString.indexOf(SS2Designator[i]) == 0) {
                            curSS2Des = i;
                            DesignatorLength = SS2Designator[i].length();
                            break;
                        }
                    }
                    if (i == SS2Designator.length) {
                        for (i = 0; i < SS3Designator.length; i++) {
                            if (tmpString.indexOf(SS3Designator[i]) == 0) {
                                curSS3Des = i;
                                DesignatorLength = SS3Designator[i].length();
                                break;
                            }
                        }
                        if (i == SS3Designator.length) {
                            switch (remainByte[1]) {
                            case ISO_SS2_7:
                                output[charOff] = getUnicode(remainByte[2],
                                            remainByte[3],
                                            SS2Flag);
                                charOff++;
                                DesignatorLength = 3;
                                break;

                            case ISO_SS3_7:
                                output[charOff] = getUnicode(remainByte[2],
                                            remainByte[3],
                                            SS3Flag);
                                charOff++;
                                DesignatorLength = 3;
                                break;

                            default:
                                DesignatorLength = 0;
                            }
                        }
                    }
                }
                if (remainIndex > DesignatorLength) {
                    for (i = 0; i < remainIndex - DesignatorLength; i++)
                        remainByte[i] = remainByte[DesignatorLength + 1 + i];
                    remainIndex = i - 1;
                } else {
                    remainIndex = -1;
                }
                break;

            default:
                if (!shiftout) {
                    output[charOff] = (char) remainByte[0];
                    charOff++;
                } else {
                    switch (state) {
                    case G0:
                        firstByte = remainByte[0];
                        state = G1;
                        break;

                    case G1:
                        output[charOff] = getUnicode(firstByte, remainByte[0],
                                    SOFlag);
                        charOff++;
                        state = G0;
                        break;
                    }
                }
                if (remainIndex > 0)
                    System.arraycopy(remainByte, 1, remainByte, 0, remainIndex);
                remainIndex--;
            }	
        }			    
        return charOff - outOff; 
    }
}    
